in this directory.
</para>
<para>
- In addition to the system repository, OSTree supports a
- per-remote
+ In addition to the system repository, OSTree supports two
+ other paths. First, there is a
+ <literal>gpgkeypath</literal> option for remotes, which must
+ point to the filename of an ASCII-armored key.
+ </para>
+ <para>Second, there is support for a per-remote
<filename><replaceable>remotename</replaceable>.trustedkeys.gpg</filename>
file stored in the toplevel of the repository (alongside
<filename>objects/</filename> and such). This is
GObject parent;
GList *keyrings;
+ GPtrArray *key_ascii_files;
};
G_DEFINE_TYPE (OstreeGpgVerifier, _ostree_gpg_verifier, G_TYPE_OBJECT)
OstreeGpgVerifier *self = OSTREE_GPG_VERIFIER (object);
g_list_free_full (self->keyrings, g_object_unref);
+ if (self->key_ascii_files)
+ g_ptr_array_unref (self->key_ascii_files);
G_OBJECT_CLASS (_ostree_gpg_verifier_parent_class)->finalize (object);
}
OstreeGpgVerifyResult *result = NULL;
gboolean success = FALSE;
GList *link;
+ int armor;
/* GPGME has no API for using multiple keyrings (aka, gpg --keyring),
* so we concatenate all the keyring files into one pubring.gpg in a
if (!g_output_stream_close (target_stream, cancellable, error))
goto out;
+ /* Save the previous armor value - we need it on for importing ASCII keys */
+ armor = gpgme_get_armor (result->context);
+ gpgme_set_armor (result->context, 1);
+
+ /* Now, use the API to import ASCII-armored keys */
+ if (self->key_ascii_files)
+ {
+ for (guint i = 0; i < self->key_ascii_files->len; i++)
+ {
+ const char *path = self->key_ascii_files->pdata[i];
+ glnx_fd_close int fd = -1;
+ ot_auto_gpgme_data gpgme_data_t kdata = NULL;
+
+ fd = openat (AT_FDCWD, path, O_RDONLY | O_CLOEXEC) ;
+ if (fd < 0)
+ {
+ glnx_set_prefix_error_from_errno (error, "Opening %s", path);
+ goto out;
+ }
+
+ gpg_error = gpgme_data_new_from_fd (&kdata, fd);
+ if (gpg_error != GPG_ERR_NO_ERROR)
+ {
+ ot_gpgme_error_to_gio_error (gpg_error, error);
+ goto out;
+ }
+
+ gpg_error = gpgme_op_import (result->context, kdata);
+ if (gpg_error != GPG_ERR_NO_ERROR)
+ {
+ ot_gpgme_error_to_gio_error (gpg_error, error);
+ goto out;
+ }
+ }
+ }
+
+ gpgme_set_armor (result->context, armor);
+
/* Both the signed data and signature GBytes instances will outlive the
* gpgme_data_t structs, so we can safely reuse the GBytes memory buffer
* directly and avoid a copy. */
self->keyrings = g_list_append (self->keyrings, g_object_ref (path));
}
+void
+_ostree_gpg_verifier_add_key_ascii_file (OstreeGpgVerifier *self,
+ const char *path)
+{
+ if (!self->key_ascii_files)
+ self->key_ascii_files = g_ptr_array_new_with_free_func (g_free);
+ g_ptr_array_add (self->key_ascii_files, g_strdup (path));
+}
+
gboolean
_ostree_gpg_verifier_add_keyring_dir (OstreeGpgVerifier *self,
GFile *path,
setup_fake_remote_repo1 "archive-z2"
-echo "1..1"
+echo "1..2"
cd ${test_tmpdir}
mkdir repo
fi
${OSTREE} pull R3:main >/dev/null
-libtest_cleanup_gpg
echo "ok"
+
+rm repo/refs/remotes/* -rf
+${OSTREE} prune --refs-only
+
+# Test the successful gpgkeypath option
+${OSTREE} remote add --set=gpgkeypath=${test_tmpdir}/gpghome/key3.asc R4 $(cat httpd-address)/ostree/gnomerepo
+${OSTREE} pull R4:main >/dev/null
+
+rm repo/refs/remotes/* -rf
+${OSTREE} prune --refs-only
+
+${OSTREE} remote add --set=gpgkeypath=${test_tmpdir}/gpghome/INVALIDKEYPATH.asc R5 $(cat httpd-address)/ostree/gnomerepo
+if ${OSTREE} pull R5:main 2>err.txt; then
+ assert_not_reached "Unexpectedly succeeded at pulling with nonexistent key"
+fi
+assert_file_has_content err.txt "INVALIDKEYPATH.*No such file or directory"
+
+rm repo/refs/remotes/* -rf
+${OSTREE} prune --refs-only
+
+${OSTREE} remote add --set=gpgkeypath=${test_tmpdir}/gpghome/key2.asc R6 $(cat httpd-address)/ostree/gnomerepo
+if ${OSTREE} pull R6:main 2>err.txt; then
+ assert_not_reached "Unexpectedly succeeded at pulling with different key"
+fi
+assert_file_has_content err.txt "GPG signatures found, but none are in trusted keyring"
+
+echo "ok"
+libtest_cleanup_gpg